# -*- coding: utf-8 -*-
import json
import logging
from datetime import datetime

from common import orm
from common.withdraw.model import WithdrawChannel, WithdrawOrder, WITHDRAW_STATUS, NOTIFY_STATUS

from common.utils import id_generator
from common.utils import exceptions as err
from common.utils.decorator import sql_wrapper

from sqlalchemy.exc import IntegrityError


_LOGGER = logging.getLogger(__name__)


@sql_wrapper
def create_order(data, chn_id=None):
    try:
        withdraw_order = WithdrawOrder.query.with_for_update().filter(
            WithdrawOrder.mch_id==data['mch_id']).filter(
            WithdrawOrder.out_trade_no==data['out_trade_no']).first()
        if not withdraw_order:
            withdraw_order = WithdrawOrder()
            withdraw_order.id = id_generator.generate_long_id('withdraw')
            withdraw_order.mch_id = data['mch_id']
            withdraw_order.service = 'alipay'
            withdraw_order.out_trade_no = data['out_trade_no']
            withdraw_order.status = WITHDRAW_STATUS.READY
            withdraw_order.channel_id = chn_id
            withdraw_order.total_fee = data['total_fee']
            withdraw_order.payee_no = data['payee_no']
            withdraw_order.payee_real_name = data['payee_real_name']
            withdraw_order.notify_url = data['notify_url']
            withdraw_order.mch_create_ip = data['mch_create_ip']
            withdraw_order.sign = data['sign']
            withdraw_order.extra = data.get('extra')
            withdraw_order.user_id = data.get('user_id', 0)
            withdraw_order.save()
        return withdraw_order
    except Exception as e:
        _LOGGER.exception('create_order error, %s', e)
        raise err.DataError()


@sql_wrapper
def get_order(order_id):
    return WithdrawOrder.query.filter(WithdrawOrder.id==order_id).first()


@sql_wrapper
def get_order_by_mch(mch_id, out_trade_no):
    return WithdrawOrder.query.filter(WithdrawOrder.mch_id==mch_id).filter(
        WithdrawOrder.out_trade_no==out_trade_no).first()


@sql_wrapper
def get_ready_orders():
    return WithdrawOrder.query.filter(WithdrawOrder.status==WITHDRAW_STATUS.READY).all()


@sql_wrapper
def get_channel(chn_id):
    return WithdrawChannel.query.filter(WithdrawChannel.id==chn_id).first()


@sql_wrapper
def get_channels_in_ids(ids):
    if not ids:
        return []
    return WithdrawChannel.query.filter(WithdrawChannel.id.in_(ids)).filter(WithdrawChannel.status == 1).all()


@sql_wrapper
def get_channels_by_mch_id(mch_id):
    return WithdrawChannel.query.filter(WithdrawChannel.mch_id == mch_id).filter(WithdrawChannel.status == 1).all()


@sql_wrapper
def update_channel_status(chn_id, status):
    datetime_now = datetime.utcnow()
    WithdrawChannel.query.filter(WithdrawChannel.id==chn_id).update({
        'status': status,
        'updated_at': datetime_now
    })
    orm.session.commit()


@sql_wrapper
def update_channel_balance(chn_id, outlay):
    chn = WithdrawChannel.query.filter(WithdrawChannel.id==chn_id).with_lockmode('update').first()
    if chn.balance is None:
        chn.balance = 0
    else:
        chn.balance = float(chn.balance) - float(outlay)
    chn.save()
    return chn


@sql_wrapper
def fill_chn(order_id, chn_id):
    datetime_now = datetime.utcnow()
    res = WithdrawOrder.query.filter(WithdrawOrder.id==order_id).filter(
        WithdrawOrder.channel_id==None).update({
        'channel_id': chn_id,
        'updated_at': datetime_now
    })
    if res:
        orm.session.commit()
        return True
    return False


@sql_wrapper
def unbind_chn(order_id):
    datetime_now = datetime.utcnow()
    res = WithdrawOrder.query.filter(WithdrawOrder.id==order_id).update({
        'channel_id': None,
        'updated_at': datetime_now
    })
    if res:
        orm.session.commit()
        return True
    return False


@sql_wrapper
def update_withdraw(order_id, sts, updated_info, trade_no=None):
    datetime_now = datetime.utcnow()
    res = WithdrawOrder.query.filter(WithdrawOrder.id == order_id).filter(
        WithdrawOrder.status == WITHDRAW_STATUS.READY).update({
            'status': sts,
            'third_id': trade_no,
            'extend': json.dumps(updated_info, ensure_ascii=False),
            'withdraw_at': datetime_now,
            'updated_at': datetime_now
        })
    if res:
        orm.session.commit()
        return True
    else:
        _LOGGER.warn('update_withdraw, cocurrency occured! order_id[%s]', order_id)
        return False


@sql_wrapper
def add_notify_success(order_id):
    withdraw_order = WithdrawOrder.query.filter(WithdrawOrder.id==order_id).with_lockmode('update').first()
    withdraw_order.notify_status = NOTIFY_STATUS.SUCC
    withdraw_order.notify_count += 1
    withdraw_order.notified_at = datetime.utcnow()
    withdraw_order.save()
    return withdraw_order


@sql_wrapper
def add_notify_fail(order_id):
    withdraw_order = WithdrawOrder.query.filter(WithdrawOrder.id==order_id).with_lockmode('update').first()
    withdraw_order.notify_status = NOTIFY_STATUS.FAIL
    withdraw_order.notify_count += 1
    withdraw_order.notified_at = datetime.utcnow()
    withdraw_order.save()
    return withdraw_order
